package com.dc.aimc.lab.util;

import com.dc.aimc.lab.config.MinIOConfig;
import com.dc.aimc.lab.entity.MinIOBucket;
import com.dc.aimc.lab.entity.MinIOFile;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

@Log4j2
@Component
public class MinIOUtil {

    @Autowired
    private MinIOConfig config;

    private MinioClient minioClient;


    /**
     * 初始化minio配置
     *
     * @param :
     * @return: void
     * @date : 2020/8/16 20:56
     */
    @PostConstruct
    public void init() {
        try {
            minioClient = MinioClient.builder().endpoint("http://" + config.getUrl()).credentials(config.getAccessKey(), config.getSecretKey()).build();
            System.out.println(minioClient == null);
        } catch (Exception e) {
            log.error("初始化minio配置异常: ", e);
        }
    }

    /**
     * 判断bucket是否存在
     * @param bucketName
     */
    public boolean existsBucket(String bucketName) {
        try {
            BucketExistsArgs args = BucketExistsArgs.builder().bucket(bucketName).build();
            return minioClient.bucketExists(args);
        } catch (Exception e) {
            log.error("判断bucket是否存在异常: ", e);
        }
        return false;
    }

    /**
     * 创建bucket
     * @param bucketName
     */
    public void createBucket(String bucketName) {
        try {
            if (existsBucket(bucketName)) {
                log.warn("bucket: {} 已存在", bucketName);
            } else {
                MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder().bucket(bucketName).build();
                minioClient.makeBucket(makeBucketArgs);
            }
        } catch (Exception e) {
            log.error("创建bucket异常: ", e);
        }
    }

    /**
     * 删除bucket(bucket须为空)
     * @param bucketName
     */
    public boolean removeBucket(String bucketName) {
        try {
            if (existsBucket(bucketName)) {
                RemoveBucketArgs removeBucketArgs = RemoveBucketArgs.builder().bucket(bucketName).build();
                minioClient.removeBucket(removeBucketArgs);
                return true;
            } else {
                log.warn("bucket: {} 不存在", bucketName);
            }
        } catch (Exception e) {
            log.error("删除bucket异常: ", e);
        }
        return false;
    }

    /**
     * bucket信息列表
     * @return
     */
    public List<MinIOBucket> listBuckets() {
        try {
            List<MinIOBucket> buckets = new ArrayList<>();
            DateTimeFormatter pattern = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            for (Bucket bucket : minioClient.listBuckets()) {
                Instant instant = bucket.creationDate().toInstant();
                String time = LocalDateTime.ofInstant(instant, ZoneOffset.ofHours(8)).format(pattern);
                buckets.add(new MinIOBucket(bucket.name(), time));
            }
            return buckets;
        } catch (Exception e) {
            log.error("获取bucket信息列表异常: ", e);
        }
        return null;
    }


    /**
     * 上传文件
     * @param bucketName
     * @param fileName
     * @param fileStream
     */
    public void uploadFile(String bucketName, String fileName, InputStream fileStream) {
        if (!existsBucket(bucketName)) {
            createBucket(bucketName);
            log.info("创建bucket: {}", bucketName);
        }
        try {
            PutObjectArgs args = PutObjectArgs.builder().bucket(bucketName).object(fileName).stream(fileStream, -1, 5 * 1024 * 1024).build();
            minioClient.putObject(args);
        } catch (Exception e) {
            log.error("上传文件异常: ", e);
        }
    }

    /**
     * 下载文件
     * @param bucketName
     * @param fileName
     * @param response
     */
    public void downLoadFile(String bucketName, String fileName, HttpServletResponse response) {
        if (!existsBucket(bucketName)) {
            log.error("bucket: {} 不存在 ", bucketName);
            return;
        }
        FilterInputStream is = null;
        ServletOutputStream os = null;
        try {
            GetObjectArgs args = GetObjectArgs.builder().bucket(bucketName).object(fileName).build();
            is = minioClient.getObject(args);
            os = response.getOutputStream();
            IOUtils.copy(is, os);
        } catch (Exception e) {
            log.error("上传文件异常: ", e);
        } finally {
            try {
                if (os != null) os.close();
                if (is != null) is.close();
            } catch (IOException e) {
                log.error("释放资源异常: ", e);
            }
        }
    }

    /**
     * 获取文件下载地址
     * @param bucketName
     * @param fileName
     */
    public String getFileUrl(String bucketName, String fileName) {
        if (!existsBucket(bucketName)) {
            log.error("bucket: {} 不存在 ", bucketName);
            return null;
        }
        try {
            GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(bucketName).object(fileName).build();
            return minioClient.getPresignedObjectUrl(args);
        } catch (Exception e) {
            log.error("上传文件异常: ", e);
        }
        return null;
    }


    /**
     * 文件信息列表
     * @param bucketName
     * @return
     */
    public List<MinIOFile> listFiles(String bucketName, String folderName) {
        if (!existsBucket(bucketName)) {
            log.error("bucket: {} 不存在 ", bucketName);
            return null;
        }
        try {
            ListObjectsArgs args = ListObjectsArgs.builder().bucket(bucketName).prefix(folderName).maxKeys(100).build();
            List<MinIOFile> results = new ArrayList<>();
            DateTimeFormatter pattern = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            for (Result<Item> itemResult : minioClient.listObjects(args)) {
                MinIOFile minIOFile = new MinIOFile();
                String name = itemResult.get().objectName();
                if (name.endsWith("/")) {
                    name = name.substring(0, name.length() - 1);
                }
                minIOFile.setName(name);
                if (itemResult.get().isDir()) {
                    minIOFile.setIsDir(true);
                } else {
                    Instant instant = itemResult.get().lastModified().toInstant();
                    String time = LocalDateTime.ofInstant(instant, ZoneOffset.ofHours(8)).format(pattern);
                    minIOFile.setTime(time);
                    long size = itemResult.get().size();
                    minIOFile.setSize(size);
                    String stringSize = convertFileSize(size);
                    minIOFile.setStringSize(stringSize);
                }
                results.add(minIOFile);
            }
            return results;
        } catch (Exception e) {
            log.error("获取文件信息列表异常: ", e);
        }
        return null;
    }

    /**
     * 字节转换
     *
     * @param size 字节大小
     * @return 转换后值
     */
    public String convertFileSize(long size) {
        long kb = 1024;
        long mb = kb * 1024;
        long gb = mb * 1024;
        long tb = gb * 1024;
        String rate = "";
        if (size >= tb) {
            rate = String.format("%.1fTB", (float) size / tb);
        } else if (size >= gb) {
            rate = String.format("%.1fGB", (float) size / gb);
        } else if (size >= mb) {
            rate = String.format("%.1fMB", (float) size / mb);
        } else if (size >= kb) {
            rate = String.format("%.1fKB", (float) size / kb);
        } else {
            rate = String.format("%d B", size);
        }
        return rate;
    }
}
